/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server;

import helper.ThreadID;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.fileInfo;

/**
 *
 * @author user
 */
public class ServerTask implements Runnable {

    private Socket server;
    private boolean accepted = false;
    private String line, input;
    public String message;
    public String usernameCurr;
    public int fileCount;
    public String workingdirCurr;
    public final String BASE_PATH = "test\\";
    public boolean timeout = false;
    public String currUserThreadID;
    public ThreadID tid;
    private Timer timer;
    public ArrayList<fileInfo> file_info = new ArrayList<fileInfo>();
    public ArrayList<fileInfo> file_info_server = new ArrayList<fileInfo>();
    public ArrayList<fileInfo> file_diff = new ArrayList<fileInfo>();

    public ServerTask(Socket server, ThreadID tid) {
        this.server = server;
        this.tid = tid;
    }

    public String ignoreWorkingDir(String full_path){
        String out = full_path.substring(this.workingdirCurr.length()+1);
        return out;
    }

    public void receiveMessage(){
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(this.server.getInputStream()));
            if ((this.message = in.readLine()) != null) {
                System.out.println(message);
            }
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public boolean cekLogin() {
        BufferedReader in = null;
         boolean c = false;
        try {
           
            //cek database
            DbHandler d = new DbHandler();
            in = new BufferedReader(new InputStreamReader(server.getInputStream()));
            if ((this.message = in.readLine()) != null){
                String[] pecah = this.message.split(";;;");

                //if (pecah[0].equalsIgnoreCase("1")){
                    this.usernameCurr = pecah[0];
                    //this.currUserThreadID = pecah[3];
                    //System.out.println(this.usernameCurr+"; Thread ID = "+this.currUserThreadID);
                    c = d.cekLogin(pecah[0], pecah[1]);
               // }
               
            }
            else{
                System.out.println("null");
            }
        } catch (IOException ex) {
            Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
        }
//         finally {
//            try {
//                in.close();
//            } catch (IOException ex) {
//                Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
//            }
//        }
        return c;
    }

    public void sendAcceptOrRejectClient() {
        if (this.cekLogin()){
            //this.sendMessage(this.usernameCurr+";login success");
            System.out.println("accept");
            this.accepted = true;
//            this.sendStartSync(usernameCurr);
            this.sendAccept(usernameCurr);
        }
     else{
           // this.sendMessage(this.usernameCurr+";login failed");
            //this.usernameCurr = null;
            System.out.println("reject");
            this.sendReject(usernameCurr);
            //start timer
//            TimeOut to = new TimeOut(5);
//            this.timer = to.timer;
//            if (this.cekLogin()){
//                to.timer.cancel();
//                this.timer.cancel();
//            }
//            else{
//
//            }
        }
    }

    public void sendStartSync(String username){
        //ngirim pesan ke client supaya mulai sinkronisasi
        this.sendMessage(this.usernameCurr+";;;startsync");
        System.out.println("start sync");
    }

    public void sendAccept(String username){
        //ngirim pesan ke client bahwa dia diterima
        this.sendMessage(this.usernameCurr+";;;accepted");
        System.out.println("accept : " +this.usernameCurr+";;;accepted");
    }

    public void sendReject(String username){
        //ngirim pesan ke client bahwa dia diterima
        this.sendMessage(this.usernameCurr+";;;rejected");
    }

    public void timeoutHandler(int interval) {
        
    }

    public void cekRequestFile(){
        String[] pecah = this.message.split(";;;");
        if (pecah[1].equalsIgnoreCase("requestfile")){
            this.workingdirCurr = pecah[2];
            //kirim fileinfo yg dari DB
            this.getFileInfoFromDB(usernameCurr);
            String infofile = "";
            //kirim pesan ke client
            if (this.file_info_server.size() != 0){
                for(int i=0; i<this.file_info_server.size(); i++){
                    infofile = infofile + this.file_info_server.get(i).getFile_path() + ";;;"
                            + this.file_info_server.get(i).getDate_modified().toString() + "%%%";
                }
            }
             else{

             }
            //System.out.println(this.usernameCurr + ";;;" + this.workingdirCurr + "###" + infofile);
            this.sendMessage(this.usernameCurr + ";;;sendingfile;;;" + this.workingdirCurr + "###" + infofile);
        }
     else{
        System.out.println("pesan rusak");
     }

    }

     public void cekUploadFile(){
         this.receiveMessage();
        String[] pecah = this.message.split(";;;");
         if (pecah[1].equalsIgnoreCase("uploadfile")){
             System.out.println("upload message = "+this.message);
             this.receiveMessage();
             this.getFileInfoFromClient();
             this.fileCount = this.file_info.size();
        
            System.out.println("upload file gan");
            System.out.println("file info size = " + this.file_info.size());
            ArrayList<String> filepaths = new ArrayList<String>();
            ArrayList<String> datemods = new ArrayList<String>();
            for(int i=0; i<this.file_info.size(); i++){
                filepaths.add(this.BASE_PATH +"client\\"+this.usernameCurr+"\\"+this.ignoreWorkingDir(this.file_info.get(i).getFile_path()));
                datemods.add(this.file_info.get(i).getDate_modified().toString());
                System.out.println("filepath : "+filepaths.get(i));
                System.out.println("datemod : "+datemods.get(i));
            }
            //this.receiveMessage();
            this.receiveFile(filepaths,datemods);
            
        }
         else{
             System.out.println("pesan rusak");
         }

    }

    public void cekDownloadFile(){
        //this.receiveMessage();
        String[] pecah = this.message.split(";;;");
        System.out.println("download message = " + this.message);
        if (pecah[1].equalsIgnoreCase("downloadfile")){
            System.out.println("client mau download");
            this.getFileInfoFromDB(usernameCurr);

            System.out.println("file path 0 = "+this.file_info_server.get(0).getFile_path());
            this.usernameCurr = pecah[0];
            this.workingdirCurr = pecah[2];
            ArrayList<String> clientpath = new ArrayList<String>();

            for (int i=0; i<this.file_info_server.size(); i++){
                String pathfromserver = this.file_info_server.get(i).getFile_path();
                System.out.println(pathfromserver);
//                String regex = "test\\\\client\\\\"+this.usernameCurr+"\\\\";
//                String clientpathstuffed = pecah[2] + "\\";
//                String replacement = clientpathstuffed.replaceAll("\\\\", "\\\\\\\\");
//                String pathforclient = pathfromserver.replaceAll(regex, replacement);
//                System.out.println("path for client ("+i+") : " + pathforclient);
//                clientpath.add(pathforclient);
            }
            this.fileCount = this.file_info_server.size();
            this.getFileInfoFromDB(usernameCurr);
            String infofile = "";
            //kirim pesan ke client
            if (!this.file_info_server.isEmpty()){
                for(int i=0; i<this.file_info_server.size(); i++){
                    infofile = infofile + this.file_info_server.get(i).getFile_path() + ";;;"
                            + this.file_info_server.get(i).getDate_modified().toString() + "%%%";
                }
            }
             else{

             }
            //System.out.println(this.usernameCurr + ";;;" + this.workingdirCurr + "###" + infofile);
            this.sendMessage(this.usernameCurr + ";;;receivingfile;;;" + this.workingdirCurr + "###" + infofile);
            this.sendMultipleFile(this.addFilePaths(file_info_server));
            this.receiveMessage();
            //kirim file yg punya si server
        }
         else{
            System.out.println("pesan rusak");
         }

    }

    public void getFileInfoFromClient(){
        String[] pecah1 = this.message.split("###");
        String[] pecah2 = pecah1[0].split(";;;");

        this.usernameCurr = pecah2[0];
        System.out.println(usernameCurr);
        this.workingdirCurr = pecah2[1];
        System.out.println(this.workingdirCurr);
        

        String[] files = pecah1[1].split("%%%");
        ArrayList<String> filesArr = new ArrayList<String>();
        filesArr.addAll(Arrays.asList(files));
        this.fileCount = filesArr.size();
        System.out.println("filecount = "+this.fileCount);
        //masukkan info file dari message
        for(int i=0; i<this.fileCount; i++){
            String[] fileinfo = filesArr.get(i).split(";;;");
            file_info.add(new fileInfo(fileinfo[0],Timestamp.valueOf(fileinfo[1])));
            System.out.println("File path : " + file_info.get(i).getFile_path());
            System.out.println("Date modified : "+file_info.get(i).getDate_modified().toString());
            System.out.println("--------------------------");
        }
    }

//    public getDownloadRequest(){
//
//    }

    public void receiveSyncRequest(){
        //fungsi yang menerima permintaan dari client
        //contoh format message =
        //username;;;working_dir###file_path;;;timestamp%%%file_path;;;timestamp

       this.getFileInfoFromClient();
        //bandingkan dengan yang di database
        this.getFileInfoFromDB(this.usernameCurr);
        //cek jumlah dan date_modified
        //cek versi
        boolean serverHasNewest = false;
        boolean clientHasNewest = false;
        for(int i=0; i<this.file_info_server.size(); i++){
           if (this.file_info_server.contains(file_info.get(i))){
               int idxfound = this.file_info_server.indexOf(file_info.get(i));
               if (this.file_info_server.get(idxfound).getDate_modified().after(file_info.get(i).getDate_modified())){
                   serverHasNewest = true;
                   //berarti client update/download file yg ini
               }
               else if (this.file_info.get(idxfound).getDate_modified().after(file_info_server.get(i).getDate_modified())){
                   clientHasNewest = true;
                   //berarti si client upload file ini ke server
               }
                 else{

                 }
           }
           else{

           }
        }
        //jika yang di client kurang atau ada file di server yang lebih baru versinya, langsung kirim file-file terbaru
        if (/*this.isServerHasMoreFile() || */serverHasNewest){
            this.sendAllFiles();
        }
        else if (clientHasNewest){
            //jika yang di client kurang namun ada file di client yang lebih baru versinya, langsung terima file yang terbaru dari client
            this.requestAllFilesToClient();
        }
         else{

         }
        
        //jika yang di client lebih banyak/sama atau ada file di client yang lebih baru, langsung terima file dari client, infonya simpen di DB
    }

    public void sendAllFiles(){
        this.getFileInfoFromDB(usernameCurr);
        for (int i=0; i<this.file_info_server.size(); i++){
            this.sendFile(this.file_info_server.get(i).getFile_path());
        }
    }

    public ArrayList<String> addFilePaths(ArrayList<fileInfo> fi){
        ArrayList<String> filepaths = new ArrayList<String>();
        for(int i=0; i<fi.size(); i++){
            filepaths.add(fi.get(i).getFile_path());
        }
        return filepaths;
    }

    public void sendMultipleFile(ArrayList<String> file_paths) {
        System.out.println("ngirim file");
        OutputStream out = null;
        try {
            out = this.server.getOutputStream();
            for (int i = 0; i < this.fileCount; i++) {
                System.out.println(file_paths.get(i));
                // sendfile
                File myFile = new File(file_paths.get(i));
                byte[] head = (myFile.length() + "").getBytes();
                int d = head.length;
                for (int k = d - 1; d < 12; d++) {
                    out.write("0".getBytes());
                }
                out.write(head);
                System.out.write(head);
                System.out.println();
                FileInputStream in = new FileInputStream(myFile);
                System.out.println("Sending...");
                byte[] mb = new byte[1024];
                for (int c = in.read(mb); c > -1; c = in.read(mb)) {
                    out.write(mb, 0, c);
                }
                Thread.sleep(500);
            }
            System.out.println("DONE!");
        } catch (InterruptedException ex) {
            Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
        } //finally {
//            try {
//                out.close();
//            } catch (IOException ex) {
//                Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
//            }
//        }

    }
    
    public void requestAllFilesToClient(){
        //kirim message buat minta file terbaru ke client
        //formatnya <username>;;;requestfile
        this.sendMessage(this.usernameCurr+";;;requestfile");
    }

    public void receiveAllFilesFromClient(){
        //terima file dari socket yg dikirim client
        //ambil current username buat pathnya
        //ambil jumlah file
        this.getFileInfoFromClient();

        for(int i=0; i<this.fileCount; i++){
        //    this.receiveFile("test\\server\\"+this.ignoreWorkingDir(this.file_info.get(i).getFile_path()));
        }
        //simpan semua infonya ke database
    }
    

    public void getFileInfoFromDB(String username){
        DbHandler db = new DbHandler();

        this.file_info_server = db.getAllFilesInfo(username);
    }

    public boolean isServerHasMoreFile(){
        return (this.file_info_server.size() >= this.file_info.size());
    }

    

    public boolean checkIsServerFileNewer(String username, fileInfo client_side){
        //ambil dari database server
        DbHandler db = new DbHandler();

        fileInfo fi = db.getFileInfo(username, client_side.getFile_path());

        return (fi.getDate_modified().after(client_side.getDate_modified()));
    }
    

    public void sendErrorMessage(String cause) {
    }

    public void sendListFileAndDir() {
    }

    public void sendMessage(String msg){
        try {

            PrintWriter out = new PrintWriter(server.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
            out.println(msg);
//            out.close();
//            in.close();
//            this.server.close();

        } catch (IOException ex) {
            Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public void sendFile(String path) {
        File f = new File(path);
        byte[] mybytearray = new byte[(int) f.length()];
        FileInputStream fis;
        try {
            fis = new FileInputStream(f);
            BufferedInputStream bis = new BufferedInputStream(fis);
            try {
                bis.read(mybytearray, 0, mybytearray.length);
            } catch (IOException ex) {
                Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
            }
            OutputStream os;
            try {

                os = this.server.getOutputStream();
                System.out.println("Sending...");
//                for(int i=0; i<100; i++){
//                    System.out.print(mybytearray[i]+"; ");
//                }
                os.write(mybytearray, 0, mybytearray.length);
                os.flush();
            } catch (IOException ex) {
                Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
            }

        } catch (FileNotFoundException ex) {
            Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    

    public void receiveFile(ArrayList<String> file_paths, ArrayList<String> date_mods) {
        InputStream in = null;
        try {
            DbHandler db = new DbHandler();
            in = this.server.getInputStream();
            for (int i = 0; i < this.fileCount; i++) {
                OutputStream out = new FileOutputStream(file_paths.get(i));
                // open a new file
                out = new FileOutputStream(file_paths.get(i));
                // read the file size (up to 12 decimal digits long)
                byte[] buf0 = new byte[12];
                int bytesRead = 0;
                while (bytesRead < 12) {
                    if (bytesRead > 0) {
                        bytesRead += in.read(buf0, bytesRead, 12 - bytesRead);
                    } else {
                        bytesRead += in.read(buf0, 0, 12);
                    }
                }
                int fileLength = 0;
                try {
                    fileLength = new Integer(new String(buf0));
                } catch (NumberFormatException e) {
                    System.out.println(e);
                    System.exit(-1);
                }
                bytesRead = 0;
                byte[] buf = new byte[1024];
                int c;
                while (bytesRead < fileLength) {
                    if (fileLength - bytesRead > 1024) {
                        c = in.read(buf, 0, 1024);
                    } else {
                        c = in.read(buf, 0, fileLength - bytesRead);
                    }
                    bytesRead += c;
                    out.write(buf, 0, c);
                }
                //out.close();
                //cek dulu, apakah recordnya udah ada atau belum?
                //kalo nggak ada, insert
                String path = file_paths.get(i).replaceAll("\\\\", "\\\\\\\\");
                boolean ada = db.isFileInfoExists(path);
                if (!ada){
                    System.out.println("gak ada");
                    System.out.println("storing to DB...");
                    System.out.println("file path "+i+": "+path);
                   db.storeOneFileInfo(usernameCurr, new fileInfo(path, Timestamp.valueOf(date_mods.get(i))));
                    System.out.println("storing one record finished!");
                    //kalo ada, update date modified doang
                }
                 else{
                    System.out.println("ada");
                    System.out.println("updating DB...");
                    db.updateFileInfoRecord(path, date_mods.get(i));
                    System.out.println("Update finished!");
                 }
                
            }

           
        } catch (IOException ex) {
            Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
        } //finally {
//            try {
//                in.close();
//            } catch (IOException ex) {
//                Logger.getLogger(ServerTask.class.getName()).log(Level.SEVERE, null, ex);
//            }
//        }
    }


    public void run() {


        input = "";

        

        this.sendAcceptOrRejectClient();
        this.receiveMessage();
        this.cekRequestFile();
        this.cekUploadFile();
        this.cekDownloadFile();
        //this.cekDownloadFile();
//         ArrayList<String> filepaths = new ArrayList<String>();
//            for(int i=0; i<this.file_info.size(); i++){
//                filepaths.add(this.BASE_PATH +"client\\"+this.usernameCurr+"\\"+this.ignoreWorkingDir(this.file_info.get(i).getFile_path()));
//               System.out.println("filepath : "+filepaths.get(i));
//            }
//            this.receiveMessage();
//            this.receiveFile(filepaths);
        //this.getFileInfoFromClient();
        
    }
}
